feat(bridge): session start-queue for extension-created sessions (#1328) - #1330
feat(bridge): session start-queue for extension-created sessions (#1328)#1330suleimansh wants to merge 2 commits into
Conversation
The daemon can queue a repo, branch and prompt; the extension claims it, drives the claude.ai new-session flow and reports the session it became. Daemon half: - bridge-starts.ts: the queue, with validation, claim-on-read and a claim that ages out so a browser quitting mid-creation retries. - GET /_bridge/start and POST /_bridge/started. - The bridge's input surface is unchanged: the daemon PRODUCES a start request, and the extension posts back only an id, a boolean and a session id. No free text is ever posted to the daemon. Page half (content.js): - createSession drives repo picker, branch picker, composer, send, and reads the session id from the URL. - probeNewSession describes the page's controls without touching them, so the first live run diagnoses instead of just failing. Refs #1328
check.mjs gains eight cases driving createSession and probeNewSession against a synthetic new-session page in jsdom: no browser, no claude.ai, no session spent. What they pin is the flow, not the markup: repo and branch are chosen before the prompt is sent, the session id is read from the URL rather than from the page, and each failure names the control it could not find. A send that never becomes a session URL is reported as a failure, because a run pointing nowhere is worse than a clean refusal. Also fixes a real bug the fixtures surfaced: the already-set check in pickFromMenu used contains-matching, so branch 'main' was satisfied by a repo trigger reading 'acme/domain-tools' and the branch was never picked at all. Exact match only for that check. Revert-proven by the case named for it. Refs #1328
|
Second commit adds the offline half of the testing story: What they pin is the flow rather than the markup: repo and branch are chosen before the prompt is sent, the session id is read from the URL rather than from anything on the page, each failure names the control it could not find, and a send that never becomes a session URL is a failure rather than a silent success. They also caught a real bug. Extension harness now 21/21. Unchanged from the first commit: the service-worker loop is still absent, so nothing drains the queue and this PR still changes no runtime behaviour. CI note: the first run of this commit went red on |
5326baf to
2145d6e
Compare
…de.ai's repo picker (#1328) (#1693) * Web runs: the Chrome extension creates the cloud session through claude.ai's repo picker (#1328) A session created through the page's repository picker is repo-bound and can push and open its pull request; `claude --cloud` has, on some accounts, produced a bundle upload that never could (#1320). So a web run now asks its daemon for an extension-created session first. Daemon: the session start-queue (`bridge-starts.ts`, re-landed from #1330) behind two faces on the one token — the extension's `GET /_bridge/start` (claim-on-read) + `POST /_bridge/started`, and the run's `POST /_web-start` + `GET /_web-start/<id>` (`web-start-endpoints.ts`), which answers 409 at once when no extension has called within three minutes. The daemon hands every spawned run its own URL in `TF_DAEMON_URL`. Driver: `CloudDriver` takes `extension: { daemonUrl, token }` (wired by the CLI from the env and the registry token); after the anchor push and trust check it posts the request — repo from the GitHub remote, the pushed ref, the whole hand-off prompt — and polls until created, then reports exactly as the CLI path does. 409/404, no GitHub remote or an unpushed ref fall back to the CLI's cloud mode with a notice; an extension that tried and failed fails the turn with its note. Extension 0.11.0 (daemon lockstep): the worker claims requests on the answer beat, one at a time, opens claude.ai/code pinned and inactive, and the content script drives the repo chip's searchable list, verifies the branch chip reads the pushed ref before sending, types the prompt, sends, and reads the session id off the address. A created session's tab is a watched tab; a failed attempt's tab is closed and the note says what the page lacked. Harness cases cover the drive, the wrong-branch refusal and the missing-picker report. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Extension: wait for the repo chip or picker to render, and treat a remembered repo's chip as the picker (#1328 dogfood 1) First live attempt: the page had remembered the last repo picked, so its chips (the-framework / main, as comboboxes) were already there — but they render after the composer, and createSession read the page before they did, then looked for a "Select repo" trigger that no longer exists once a repo is remembered. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Extension: drive the new-session page as it actually is — combobox chips in order, visible options only, React-safe search (#1328 dogfood 2) Run 2 clicked the repo chip itself as if it were a list entry (its text is the bare repo name) and then found no chip "afterwards". Read from the live page: the chips are combobox buttons in the order repo, branch, add; a picker is a dialog with a role=combobox search input and a listbox of options; a closed picker's options stay in the DOM, so only visible options are entries, and the trigger is never one. The branch list does offer a ref pushed seconds earlier (checked live), so verifying the chip before sending is enough. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Extension: wait for the picker's list to load before filtering it, fall back to the whole list, and say what the list showed on failure (#1328 dogfood 3) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Harness: a list that already holds the entry is not filtered first Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Extension: read chip and entry labels through the page's icon-font glyphs (#1328 dogfood 4) In the content script's world the labels carry private-use glyphs — the chips read "\ue048the-framework" and "\ue078main", the chosen entry "framework/the-framework\ue03b" — so no exact match ever fired. The mirror already strips those; the control labels now do too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
Superseded by #1693 (merged): the daemon half re-landed on today's paths, the extension half built and dogfooded. 🤖 curated |
Refs #1328. Incomplete on purpose - see "What is missing" before reviewing.
What is here
Daemon half (complete, tested). A start-queue the daemon fills and the extension drains.
bridge-starts.ts: validation (owner/name, a git branch name, a bounded prompt), claim-on-read, and a claim that ages out after 90s so a browser quitting mid-creation retries instead of bricking the request.GET /_bridge/startclaims the next request as it serves it. A GET that mutates, which is unusual and deliberate: handing the same request to two polling tabs would create two cloud sessions on the user's account.POST /_bridge/startedtakes back an id, a boolean, an optional session id and a note. Success without a session id is recorded as a failure, because a run pointing nowhere is not a usable outcome.The bridge's "deliberately tiny input" rule is intact. A start request carries the free text that rule excludes, and it stays excluded because the direction is outward: the daemon produces it, the extension reads it, and what comes back is only an id, a boolean and a session id. Nothing here widens what someone holding the bridge token can say.
Page half (
content.js).createSessiondrives the repo picker, the branch picker, the composer and send, then reads the session id from the URL - the one thing claude.ai is guaranteed to tell us and exactly what the daemon joins runs on.probeNewSessiondescribes the page's controls without touching them.Every selector in there is a guess about someone else's UI, so the code is built to report rather than insist: each failure names the control it could not find, and the probe exists so the first live run diagnoses instead of just failing.
What is missing
The service-worker loop in
background.js- poll/_bridge/start, open the new-session tab, hand the request to the content script, report back. My tooling refused to write it (a permission classifier blocked the edit), so it is deliberately absent rather than forgotten. Without it nothing drains the queue, so this PR changes no runtime behaviour: the routes exist, the queue exists, and nobody fills or empties it.Also not started, both named in #1328:
CloudDriver's extension-backed mode (hand-off resolving on the bridge report), and the concurrency question. On the concurrency point the honest answer from the page half is that creation is inherently serial - the flow navigates one new-session page, so ten sessions are ten sequential creations, a throughput question rather than a correctness one.The policy question is still open
#1328 records that this direction needs the Usage Policy angle settled deliberately. My reading is that Consumer Terms section 3 does not permit it, and that the "user-installed extension in your own browser" arguments are not carve-outs; the two carve-outs are an API key or explicit permission. That is written up in full for the issue. This PR is the code that exists, not a verdict that the direction is settled.
Checks
Framework suite 1535/0, typecheck clean. Extension harness (
node check.mjs) 13/13, unchanged by the content.js additions.